using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

using GoblinXNA;
using GoblinXNA.SceneGraph;
using GoblinXNA.Graphics;
using GoblinXNA.Graphics.Geometry;
using GoblinXNA.Device.Generic;
using GoblinXNA.Physics;
#if WINDOWS
using GoblinXNA.Physics.Newton1;
#else
using GoblinXNA.Physics.Matali;
#endif
using GoblinXNA.UI.UI2D;

namespace SplashOn_PhoneLib
{
    /// <summary>
    /// This tutorial demonstrates how to select 3D objects using the wrapped Newton physics library.
    /// </summary>
    public class SplashOn_ObjectManipulation
    {
        GraphicsDeviceManager graphics;
        Scene scene;
        SpriteFont textFont;

        int objectSelected = -2;

        String label = "Let's pick";
        TransformNode boxTransNode;
        Vector3 vec = new Vector3(0, 0, 0);
        float fac = 1;
        float facshuttle = 1;
        float rot1 = 0;
        float rot2 = 0;
        float rot3 = 0;
        Viewport viewport;
        Color text;

        int time = 0;
        bool BoxMani = false;
        bool RotationIsReady = false;
        bool VIsReady = false;

        TransformNode coneTransNode;
        TransformNode torusTransNode;
        TransformNode shuttleTransNode;

        GeometryNode shuttleNode;
        GeometryNode coneNode;
        GeometryNode torusNode;

        int sign = 0;
        int direct = 0;

        public SplashOn_ObjectManipulation()
        {
            //graphics = new GraphicsDeviceManager(this);
            //Content.RootDirectory = "Content";
            text = Color.White;


        }

        public Texture2D VideoBackground
        {
            get { return scene.BackgroundTexture; }
            set { scene.BackgroundTexture = value; }
        }
        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        /// 
        public void Initialize(IGraphicsDeviceService service, ContentManager content)
        {
            // Initialize the GoblinXNA framework
            State.InitGoblin(service, content, "");

            LoadContent(content);
            viewport = service.GraphicsDevice.Viewport;

            // Initialize the scene graph
            scene = new Scene();

            // Set the background color to CornflowerBlue color. 
            // GraphicsDevice.Clear(...) is called by Scene object with this color. 
            scene.BackgroundColor = Color.Black;


            // We will use the Matali physics engine ()
            // for processing the intersection of cast rays with
            // 3D objects to detect object selection. 
            scene.PhysicsEngine = new MataliPhysics();


            // Set up the lights used in the scene
            CreateLights();

            // Set up the camera which defines the eye location and viewing frustum
            CreateCamera();

            // Create 3D objects
            CreateObjects();

            // Add a mouse click callback function to perform ray picking when mouse is clicked

        }

        public int getObjectSelected()
        {
            return objectSelected;
        }

        public void MouseInputInit()
        {
            MouseInput.Instance.MouseClickEvent += new HandleMouseClick(MouseClickHandler);
        }

        private void CreateLights()
        {
            // Create a directional light source
            LightSource lightSource = new LightSource();
            lightSource.Direction = new Vector3(1, -1, -1);
            lightSource.Diffuse = Color.White.ToVector4();
            lightSource.Specular = new Vector4(0.6f, 0.6f, 0.6f, 1);

            // Create a light node to hold the light source
            LightNode lightNode = new LightNode();
            lightNode.LightSource = lightSource;

            // Add this light node to the root node
            scene.RootNode.AddChild(lightNode);

            LightSource lightSource1 = new LightSource();
            lightSource1.Direction = new Vector3(-1, 1, -1);
            lightSource1.Diffuse = Color.White.ToVector4();
            lightSource1.Specular = new Vector4(0.6f, 0.6f, 0.6f, 1);

            // Create a light node to hold the light source
            LightNode lightNode1 = new LightNode();
            lightNode1.LightSource = lightSource1;

            // Add this light node to the root node
            scene.RootNode.AddChild(lightNode1);
        }

        private void CreateCamera()
        {
            // Create a camera
            Camera camera = new Camera();
            // Put the camera at (0, 0, 10)
            camera.Translation = new Vector3(0, 4, 10);
            // Rotate the camera -20 degrees about the X axis
            camera.Rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitX,
                MathHelper.ToRadians(-20));
            // Set the vertical field of view to be 45 degrees
            camera.FieldOfViewY = MathHelper.ToRadians(45);
            // Set the near clipping plane to be 0.1f unit away from the camera
            camera.ZNearPlane = 0.1f;
            // Set the far clipping plane to be 1000 units away from the camera
            camera.ZFarPlane = 1000;

            // Now assign this camera to a camera node, and add this camera node to our scene graph
            CameraNode cameraNode = new CameraNode(camera);
            scene.RootNode.AddChild(cameraNode);

            // Assign the camera node to be our scene graph's current camera node
            scene.CameraNode = cameraNode;
        }

        private void CreateObjects()
        {
            // Create a geometry node with a model of box

            torusNode = new GeometryNode("Torus");
            torusNode.Model = new Torus(0.5f, 1.2f, 30, 30);

            Material torusMat = new Material();
            torusMat.Diffuse = Color.Yellow.ToVector4();
            torusMat.Specular = Color.White.ToVector4();
            torusMat.SpecularPower = 10;

            torusNode.Material = torusMat;

            torusTransNode = new TransformNode();
            torusTransNode.Translation = new Vector3(4, 2.5f, 0);

            // Since GoblinXNA does not have a predefined shape type
            // of torus, we define the shape to be ConvexHull,
            // which is applicable to any shape, but more computationally
            // expensive compared to the other primitive shape types we have
            // used thus far
            torusNode.Physics.Shape = GoblinXNA.Physics.ShapeType.ConvexHull;
            torusNode.Physics.Pickable = true;
            torusNode.AddToPhysicsEngine = true;

            scene.RootNode.AddChild(torusTransNode);
            torusTransNode.AddChild(torusNode);

            coneNode = new GeometryNode("Cone");
            // Note that cone is a special case of a cylinder with top
            // radius of 0
            coneNode.Model = new Cylinder(1.5f, 0, 3f, 20);

            Material coneMat = new Material();
            coneMat.Diffuse = Color.Cyan.ToVector4();
            coneMat.Specular = Color.White.ToVector4();
            coneMat.SpecularPower = 5;

            coneNode.Material = coneMat;

            coneTransNode = new TransformNode();
            coneTransNode.Translation = new Vector3(0, -1, 0);

            coneNode.Physics.Shape = GoblinXNA.Physics.ShapeType.Cone;
            coneNode.Physics.Pickable = true;
            coneNode.AddToPhysicsEngine = true;

            scene.RootNode.AddChild(coneTransNode);
            coneTransNode.AddChild(coneNode);


            shuttleNode = new GeometryNode("Cylinder");

            /*   ModelLoader loader = new ModelLoader();
               IModel splashBlobModel = loader.Load("", "p1_wedge");
               ((GoblinXNA.Graphics.Model)splashBlobModel).UseInternalMaterials = true; */
            //     splashBlobModel.UseInternalMaterials = true;

            shuttleNode.Model = new Cylinder(1.5f, 1.5f, 3f, 20); // splashBlobModel;

            shuttleTransNode = new TransformNode();
            shuttleTransNode.Translation = new Vector3(-4, 1.5f, 0);

            Material shuttleMaterial = new Material();
            shuttleMaterial.Diffuse = Color.SeaGreen.ToVector4();
            shuttleMaterial.Specular = Color.SeaGreen.ToVector4();
            shuttleMaterial.SpecularPower = 1;

            shuttleNode.Material = shuttleMaterial;

            // Since GoblinXNA does not have a predefined shape type
            // of torus, we define the shape to be ConvexHull,
            // which is applicable to any shape, but more computationally
            // expensive compared to the other primitive shape types we have
            // used thus far
            shuttleNode.Physics.Shape = GoblinXNA.Physics.ShapeType.ConvexHull;
            shuttleNode.Physics.Pickable = true;
            shuttleNode.AddToPhysicsEngine = true;

            scene.RootNode.AddChild(shuttleTransNode);
            shuttleTransNode.AddChild(shuttleNode);

        }

        public void MouseClickHandler(int button, Point mouseLocation)
        {
            // Only perform the ray picking if the clicked button is LeftButton
            if (button == 1)
            {
                // In order to perform ray  picking, first we need to define a ray by projecting
                // the 2D mouse location to two 3D points: one on the near clipping plane and one on
                // the far clipping plane.  The vector between these two points defines the finite-length
                // 3D ray that we wish to intersect with objects in the scene.

                // 0 means on the near clipping plane, and 1 means on the far clipping plane
                Vector3 nearSource = new Vector3(mouseLocation.X, mouseLocation.Y, 0);
                Vector3 farSource = new Vector3(mouseLocation.X, mouseLocation.Y, 1);

                // Now convert the near and far source to actual near and far 3D points based on our eye location
                // and view frustum

                Vector3 nearPoint = viewport.Unproject(nearSource,
                    State.ProjectionMatrix, State.ViewMatrix, Matrix.Identity);
                Vector3 farPoint = viewport.Unproject(farSource,
                    State.ProjectionMatrix, State.ViewMatrix, Matrix.Identity);

                // Have the physics engine intersect the pick ray defined by the nearPoint and farPoint with
                // the physics objects in the scene (which we have set up to approximate the model geometry).
#if WINDOWS
                List<PickedObject> pickedObjects = ((NewtonPhysics)scene.PhysicsEngine).PickRayCast(
                    nearPoint, farPoint);
#else
                List<PickedObject> pickedObjects = ((MataliPhysics)scene.PhysicsEngine).PickRayCast(
                    nearPoint, farPoint);
#endif

                // If one or more objects intersect with our ray vector
                if (pickedObjects.Count > 0)
                {
                    // Since PickedObject can be compared (which means it implements IComparable), we can sort it in 
                    // the order of closest intersected object to farthest intersected object
                    pickedObjects.Sort();

                    // We only care about the closest picked object for now, so we'll simply display the name 
                    // of the closest picked object whose container is a geometry node

                    label = ((GeometryNode)pickedObjects[0].PickedPhysicsObject.Container).Name + " is picked. Pinch to scale.";
                    objectSelected = 1;
                    // NOTE: for a shape defined as ConvexHull (e.g.,the torus shape), even if you click the
                    // hole in the torus, it will think that it is picked. This is because a ConvexHull shape
                    // does not have holes, and the physics engine we use does not support shape with holes.
                    // However, it is possible to refine this behavior by performing your own ray intersection algorithm
                    // on this picked object. It's a good idea to perform your ray intersection after the
                    // physics engine returns you a picked object, since the physics engine's algorithm is well
                    // optimized. Then, you can work your way from the front of the pickedObjects list to the
                    // back, performing your own ray intersection with each object in sequence,
                    // until you find an object that it intersects.
                    // If you want to implement your own picking algorithm, we suggest that you see the 
                    // "Picking with Triangle-Accuracy Sample" at http://creators.xna.com/Education/Samples.aspx
                }
                else
                    label = "Nothing is selected";
            }
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        private void LoadContent(ContentManager content)
        {
            textFont = content.Load<SpriteFont>("Sample");

        }


        public void Dispose()
        {
            scene.Dispose();
        }



        public void GetDelta(Vector2 vect)
        {

            vec.X = vec.X + (float)0.035 * vect.X;
            vec.Y = vec.Y - (float)0.035 * vect.Y;


        }

        public void GetDelta(float facc)
        {
            fac = fac + (float)0.02 * facc;
            facshuttle = facshuttle + (float)0.00009 * facc;
        }

        public void GetDelta(float rott, float rottt, float rotttt, int dir)
        {
            direct = dir;
            rot1 = (float)(rott * 0.01);
            rot2 = (float)(rottt * 0.01);
            rot3 = (float)(rotttt * 0.01);
            System.Diagnostics.Debug.WriteLine("rot1:" + rot1.ToString());
            System.Diagnostics.Debug.WriteLine("rot2:" + rot2.ToString());
            System.Diagnostics.Debug.WriteLine("rot3:" + rot3.ToString());
        }



        public void Update(TimeSpan elapsedTime, bool isActive)
        {

            time = time + 1;
            if (direct == 0)
            {
                if (label == "Torus is picked. Pinch to rotate.")
                    label = "Torus is picked. Pinch to scale.";
                if (label == "Cylinder is picked. Pinch to rotate.")
                    label = "Cylinder is picked. Pinch to scale.";
                if (label == "Cone is picked. Pinch to rotate.")
                    label = "Cone is picked. Pinch to scale.";
            }
            if (direct != 0)
            {
                if (label == "Torus is picked. Pinch to scale.")
                    label = "Torus is picked. Pinch to rotate.";
                if (label == "Cylinder is picked. Pinch to scale.")
                    label = "Cylinder is picked. Pinch to rotate.";
                if (label == "Cone is picked. Pinch to scale.")
                    label = "Cone is picked. Pinch to rotate.";
            }
            if (label == "Torus is picked. Pinch to scale." || label == "Torus is picked. Pinch to rotate.")
            {
                sign = 1;
                text = Color.Yellow;
                torusNode.AddToPhysicsEngine = false;
                torusTransNode.Translation = new Vector3(0, 0, 0);
                shuttleNode.AddToPhysicsEngine = false;
                coneNode.AddToPhysicsEngine = false;
                shuttleTransNode.Translation = new Vector3(100, 100, 100);
                coneTransNode.Translation = new Vector3(100, 100, 100);
                if (direct == 0)
                {
                    torusTransNode.Translation = vec;
                    torusTransNode.Scale = new Vector3(fac, fac, fac);
                }
                else
                {
                    torusTransNode.Translation = vec;
                    torusTransNode.Rotation = Quaternion.CreateFromYawPitchRoll(rot1, rot2, rot3);
                }


            }

            if (label == "Cone is picked. Pinch to scale." || label == "Cone is picked. Pinch to rotate.")
            {
                sign = 1;
                text = Color.Cyan;
                coneNode.AddToPhysicsEngine = false;
                coneTransNode.Translation = new Vector3(0, 0, 0);
                torusNode.AddToPhysicsEngine = false;
                torusTransNode.Translation = new Vector3(100, 100, 100);
                shuttleNode.AddToPhysicsEngine = false;
                shuttleTransNode.Translation = new Vector3(100, 100, 100);

                if (direct == 0)
                {
                    coneTransNode.Translation = vec;
                    coneTransNode.Scale = new Vector3(fac, fac, fac);
                }


                else
                {
                    coneTransNode.Translation = vec;
                    coneTransNode.Rotation = Quaternion.CreateFromYawPitchRoll(rot1, rot2, rot3);
                }

            }
            if (label == "Cylinder is picked. Pinch to scale." || label == "Cylinder is picked. Pinch to rotate.")
            {
                sign = 1;
                text = Color.SeaGreen;
                coneNode.AddToPhysicsEngine = false;
                coneTransNode.Translation = new Vector3(100, 100, 100);
                torusNode.AddToPhysicsEngine = false;
                shuttleNode.AddToPhysicsEngine = false;
                shuttleTransNode.Translation = new Vector3(0, 0, 0);


                torusTransNode.Translation = new Vector3(100, 100, 100);

                if (direct == 0)
                {
                    shuttleTransNode.Translation = vec;
                    shuttleTransNode.Scale = new Vector3(fac, fac, fac);
                }
                else
                {
                    // shuttleTransNode.Translation = vec;
                    shuttleTransNode.Rotation = Quaternion.CreateFromYawPitchRoll(rot1, rot2, rot3);
                }
            }

            scene.Update(elapsedTime, false, isActive);

        }

        public void Draw(TimeSpan elapsedTime)
        {
            UI2DRenderer.WriteText(new Vector2(20, 5), label, text,
               textFont, GoblinEnums.HorizontalAlignment.Center, GoblinEnums.VerticalAlignment.Top);



            //State.Device.Viewport = viewport;
            scene.Draw(elapsedTime, false);
            if (direct == 0)
            {

                UI2DRenderer.WriteText(new Vector2(590, 430), "Let's Rotate!", text, textFont);
            }

            if (direct == 1)
            {
                UI2DRenderer.WriteText(new Vector2(700, 430), "Yaw", text,
                textFont);
                // UI2DRenderer.DrawLine(750, 420, 750, 460, Color.Red, 4);
                //UI2DRenderer.DrawLine(730, 440, 770, 440, Color.Red, 4);
            }
            if (direct == 2)
            {
                UI2DRenderer.WriteText(new Vector2(700, 430), "Pitch", text,
                textFont);
                //UI2DRenderer.DrawLine(750, 420, 750, 460, Color.Yellow, 4);
                //UI2DRenderer.DrawLine(730, 440, 770, 440, Color.Yellow, 4);
            }
            if (direct == 3)
            {
                UI2DRenderer.WriteText(new Vector2(700, 430), "Roll", text,
                textFont);
                //UI2DRenderer.DrawLine(750, 420, 750, 460, Color.Yellow, 4);
                //UI2DRenderer.DrawLine(730, 440, 770, 440, Color.Yellow, 4);
            }
            if (label == "Torus is picked. Pinch to scale." || label == "Torus is picked. Pinch to rotate.")
            {
                //System.Diagnostics.Debug.WriteLine("!!!");
                UI2DRenderer.DrawCircle(40, 400, 40, Color.White);
                UI2DRenderer.DrawCircle(40, 400, 25, Color.White);
                UI2DRenderer.FillCircle(40, 400, 40, Color.Yellow);
                UI2DRenderer.FillCircle(40, 400, 25, Color.Black);
            }
            if (label == "Cylinder is picked. Pinch to scale." || label == "Cylinder is picked. Pinch to rotate.")
            {
                //System.Diagnostics.Debug.WriteLine("!!!");
                Rectangle rec = new Rectangle(40, 410, 40, 65);
                UI2DRenderer.DrawRectangle(rec, Color.White, 1);
                UI2DRenderer.FillRectangle(rec, null, Color.SeaGreen);
            }
            if (label == "Cone is picked. Pinch to scale." || label == "Cone is picked. Pinch to rotate.")
            {
                //System.Diagnostics.Debug.WriteLine("!!!");

                UI2DRenderer.DrawLine(40, 400, 15, 460, Color.Black, 1);
                UI2DRenderer.DrawLine(15, 460, 65, 460, Color.Black, 1);
                UI2DRenderer.DrawLine(65, 460, 40, 400, Color.Black, 1);
                Point p1 = new Point(40, 400);
                Point p2 = new Point(15, 460);
                Point p3 = new Point(65, 460);
                List<Point> poly = new List<Point>();
                poly.Add(p1);
                poly.Add(p2);
                poly.Add(p3);
                UI2DRenderer.FillPolygon(poly, Color.Cyan);

            }
        }

        public void GetGeo()
        {
            GeometryNode temp = new GeometryNode();


            if (label.StartsWith("Torus is picked"))
            {
                temp = torusNode;
            }

            if (label.StartsWith("Cone is picked"))
            {
                temp = coneNode;
            }

            if (label.StartsWith("Cylinder is picked"))
            {
                temp = shuttleNode;
            }

            Objects.selectedObjectGeometryNode = temp;
        }
        public void GetTrans()
        {

            TransformNode temp = new TransformNode(); // = cubeTransNode;
            if (label.StartsWith("Cylinder is picked"))
            {
                temp = shuttleTransNode;
            }

            if (label.StartsWith("Torus is picked"))
            {
                temp = torusTransNode;
            }

            if (label.StartsWith("Cone is picked"))
            {
                temp = coneTransNode;
            }
            Objects.selectedObjectTransformNode = temp;
        }
    }
}